
#include "cc_device_msg.h"
#include "cc_shell.h"
#include "cc_msg.h"

#if (CC_CONFIG_USE_DEVIEMSG == 1)
extern user_device devices[CC_CONFIG_DEVICE_MAX_NUM];
device_msg_distr msg_distr;
uint8_t cc_device_msg_debug = 0;

const char DEVICE_DISTR_FRAME_HEAD[] = CC_CONFIG_DEVICE_DISTR_HEAD;

static void device_msg_help(char *param, int node)
{
  char resp[1000];
  char temp[100];
  sprintf(resp, "-----help doc of device distr-----\r\n");
  sprintf(temp, "1:frame head:%s\r\n", DEVICE_DISTR_FRAME_HEAD);
  strcat(resp, temp);
  strcat(resp, "2:frame format:\r\n  cmd,dtype,did,ctype,aync,data, \r\n");
  strcat(resp, "-------------------------------------------\r\n");
  cc_msg_resp(node, resp, 1000, CC_NULL);
}
CC_COMMAND_SHELL_ALIAS(device_msg_help, "d_distr_help",
                       "help of device msg distr")
//  cmd   d_type  did  c_type aync  msgdata
//   1  |   2   |  2  |  2  |  1  | ...max:20  |
static void cc_msg_convert_state(char *src, cc_device_msg_t msg, int len, int data_len)
{
  char data[20];
  memset(data, 0, sizeof(data));
  static int state;
  src -= len;
  src--;
  state++;
  switch (state)
  {
  case CC_MSG_TYPE_CMD:
    if (cc_device_msg_debug)
      cc_log("start msg dpackge\r\n");
    memcpy(data, src, len);
    msg->cmd = atoi(data);
    break;
  case CC_MSG_TYPE_DTYPE:
    memcpy(data, src, len);
    msg->device_type = atoi(data);
    break;
  case CC_MSG_TYPE_DID:
    memcpy(data, src, len);
    msg->device_id = atoi(data);
    break;
  case CC_MSG_TYPE_CTYPE:
    memcpy(data, src, len);
    msg->ctrl_type = atoi(data);
    break;
  case CC_MSG_TYPE_AYNC:
    memcpy(data, src, len);
    msg->is_aync = atoi(data);
    break;
  case CC_MSG_TYPE_DATA:
    memcpy(msg->data, src, data_len);
    state = CC_MSG_TYPE_RESET_STATE;
    if (cc_device_msg_debug)
      cc_log("msg dpackge success\r\n");
    break;
  default:
    state = CC_MSG_TYPE_RESET_STATE;
    break;
  }

  src += len;
  src++;
}

int cc_msg_is_valid(char *src)
{
  int len;
  while (src[0] != '\0')
  {
    src++;
    len++;
  }
  src--;
  if (src[0] != ',')
  {
    src -= (--len);
    return 0;
  }
  src -= (--len);
  return 1;
}

//  cmd   d_type  did  c_type is aync  msg
//   1  |   2   |  2  |  2  |  2     | ...
int cc_msg_to_mqttmsg(char *src, cc_device_msg_t msg, int data_len)
{
  // TODO 可以上锁保护 并且可以改变成链表一条条取出来解析的形式
  // TODO 不过目前没必要 因为消息接收频率本来就很低
  int i = 0;
  int len = 0;
  int state = 0;
  int flag = 0;
  msg->data = CC_NULL;
  if (!msg)
  {
    cc_log_e("mqtt_msg is null\r\n");
    return -1;
  }
  // 首先检测数据是否符合格式 结尾要有逗号
  if (!cc_msg_is_valid(src))
  {
    cc_log_e("msg is not vaild\r\n");
    msg->cmd = -1;
    msg->ctrl_type = -1;
    msg->device_id = -1;
    msg->device_type = -1;
    return -1;
  }

  const char s[2] = ","; // 可以是一个分隔符，也可以是分隔符的集合
  char *token;
  token = strtok(src, s);
  int cnt = 0;
  /* 继续获取其他的子字符串 */
  char param[5][5];
  while (token != NULL)
  {
    if (cnt > 4)
    {
      break;
    }
    if (cc_device_msg_debug == 1)
      cc_log("split data:%s\r\n", token);
    if (strlen(token) > 5)
    {
      cc_log_e("data to large:%d\r\n", cnt);
      return -1;
    }
    memcpy(param[cnt], token, 5);
    cnt++;
    token = strtok(NULL, s);
  }

  if (cnt < 5)
  {
    cc_log_e("data frame is lack param\r\n");
    return -1;
  }
  msg->cmd = atoi(param[0]);
  msg->device_type = atoi(param[1]);
  msg->device_id = atoi(param[2]);
  msg->ctrl_type = atoi(param[3]);
  msg->is_aync = atoi(param[4]);
  if (cnt == 5)
  {
    msg->data = (char *)cc_malloc(strlen(token) + 1);
    memset(msg->data, 0, strlen(token) + 1);
    memcpy(msg->data, token, strlen(token));
  }
  else
  {
    if (cc_device_msg_debug)
      cc_log("no data domain frame\r\n");
  }

  if (cc_device_msg_debug)
    cc_log("frame handle success\n cmd:%2d  d_type:%2d did:%2d c_type:%2d\r\n "
           "data:%s\r\n",
           msg->cmd, msg->device_type, msg->device_id, msg->ctrl_type, msg->data);
  return 1;
}

void cc_msg_exe_device_cb(user_device_t device, cc_device_msg_t msg)
{
  if (msg->cmd == CC_MSG_CMD_READ)
  {
    if (device->read_cb)
      device->read_cb(device, msg->data, msg->ctrl_type, CC_NULL);
    else
      cc_log_e("device(type:%d id:%d do not have read cb)", device->device_type,
               device->device_id);
    if (device->read_exit_cb)
      device->read_exit_cb(msg);
  }
  if (msg->cmd == CC_MSG_CMD_CONTROL)
  {
    if (device->control_cb)
      device->control_cb(device, msg->data, msg->ctrl_type, CC_NULL);
    else
      cc_log_e("device(type:%d id:%d do not have control cb)",
               device->device_type, device->device_id);
    if (device->control_exit_cb)
      device->control_exit_cb(msg);
  }

  if (msg->cmd == CC_MSG_CMD_WRITE)
  {
    if (device->write_cb)
      device->write_cb(device, msg->data, msg->ctrl_type, CC_NULL);
    else
      cc_log_e("device(type:%d id:%d do not have write cb)",
               device->device_type, device->device_id);

    if (device->write_exit_cb)
      device->write_exit_cb(msg);
  }
}

void cc_msg_distr(user_device_t devices, cc_device_msg_t msg)
{
  // TODO 需要锁保护
  user_device_t device =
      cc_device_get(devices, msg->device_type, msg->device_id);
  if (msg->cmd < 0 || msg->ctrl_type < 0 || msg->device_id < 0 || msg->cmd < 0)
  {
    cc_log_e("the frame dpacket failed,check frame or frame handle fun\r\n");
    return;
  }
  if (device == CC_NULL)
  {
    cc_log_e("cat not get device(type:%d id:%d) is it register?\r\n",
             msg->device_type, msg->device_id);
    return;
  }
  // 直接调用回调形式同步执行
  // 也可以通过消息队列发送给对应的任务异步执行
  if (msg->is_aync == CC_MSG_AYNC)
  {
    // 发送到对应设备的消息队列中 这里不能永久阻塞
    // 否则一个设备的队列发送不进去，分发层就会卡死
    if (cc_device_msg_debug)
      cc_log("device(type:%d id:%d):aync exe\r\n", msg->device_type,
             msg->device_id);
    cc_mutex_lock(&device->mutex);
    // 设备内部要记得free(msg->data) 因为这个data是动态的;
    if (&device->mqtt_queue)
      cc_queue_send_time(&device->mqtt_queue, &msg, 50);
    else
      cc_log_e("the device do not init queue so that it can not exe aync\r\n");
    cc_mutex_unlock(&device->mutex);
  }
  else if (msg->is_aync == CC_MSG_SYNC)
  {
    if (cc_device_msg_debug)
      cc_log("device(type:%d id:%d):sync exe data:%s\r\n", msg->device_type,
             msg->device_id, msg->data);
    cc_msg_exe_device_cb(device, msg);
  }
  else
  {
    cc_free(msg->data);
    cc_log_e("please use 0 or 1 to set aync or sync\r\n");
  }
}

void cc_device_distr_data_handle(cc_mnode_t data_node)
{
  cc_msg_frame *data_frame;
  data_frame = (cc_msg_frame *)data_node->mem;
  char *data = data_frame->data;
  cc_device_msg device_frame;
  data += sizeof(DEVICE_DISTR_FRAME_HEAD) - 1;
  if (cc_device_msg_debug)
    cc_log("start device msg distr msg:%s\r\n", data);
  if (cc_msg_to_mqttmsg(data, &device_frame, data_frame->data_len) != 1)
  {
    if (device_frame.data != CC_NULL)
      cc_free(device_frame.data);
    return;
  }
  if (cc_queue_send_time(msg_distr.que, &device_frame, 1000) == cc_true)
  {
    if (cc_device_msg_debug)
      cc_log("msg send to distr layer\r\n");
  }
}

// 根据 mqtt 发送的消息 分发至对应的设备中
void cc_device_msg_distr_entry(void *data)
{
  cc_device_msg frame;
  if (cc_device_msg_debug)
    cc_log("into\r\n");
  while (1)
  {
    cc_mutex_lock(msg_distr.mut);
    if (cc_true == cc_queue_recv_time(msg_distr.que, &frame, 100))
    {

      if (cc_device_msg_debug)
        cc_log("msg sucess get\r\n");
      cc_msg_distr(devices, &frame);
      memset(&frame, 0, sizeof(frame));
    }
    cc_mutex_unlock(msg_distr.mut);
  }
}
CC_THREAD_REGISTER_ALIAS(cc_device_msg_distr_entry, 2, 2048, "d_msg_distr");

int cc_device_msg_init()
{
  msg_distr.devices = devices;
  msg_distr.que = cc_queue_create(20, sizeof(cc_device_msg));
  msg_distr.mut = cc_mutex_create();
  return 1;
}
CC_INIT_APP_REGISTER(cc_device_msg_init)

#if (LOG_D == 1)
void device_msg_debug(char *param, int node)
{
  cc_log_d("recv from node(%d):%s\r\n", node, param);
  if (cc_is_all_digits(param))
  {
    cc_device_msg_debug = atoi(param);
    if (cc_device_msg_debug < 0 || cc_device_msg_debug > 1)
    {
      cc_log_e("need 1 or 0\r\n");
      return;
    }
  }
  else
  {
    cc_log_e("need all digit param\r\n");
    return;
  }

  if (cc_device_msg_debug)
  {
    cc_msg_resp(node, "device_msg debug open\r\n", 0, 0);
  }
  else
  {
    cc_msg_resp(node, "device_msg debug close\r\n", 0, 0);
  }
}
CC_COMMAND_SHELL(device_msg_debug, "enable(1) disable(0) device_msg debug printf");
#endif
#endif
